using System;
using System.Net.Http;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using MES_workbench.ErrorCode;

namespace MES_workbench.Api.Controllers
{
    /// <summary>
    /// 天气服务控制器（基于 Open-Meteo，免 Key）
    /// </summary>
    [ApiController]
    [Route("api/[controller]")]
    public class WeatherController : ControllerBase
    {
        private readonly ILogger<WeatherController> _logger;
        private readonly IMemoryCache _cache;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly string _owApiKey;

        public WeatherController(ILogger<WeatherController> logger, IMemoryCache cache, IHttpClientFactory httpClientFactory, IConfiguration configuration)
        {
            _logger = logger;
            _cache = cache;
            _httpClientFactory = httpClientFactory;
            // 优先从配置/环境变量读取；最后兜底为之前的常量值（不推荐在生产使用）
            _owApiKey =
                configuration["OpenWeather:ApiKey"]
                ?? configuration["WeatherApi:ApiKey"]
                ?? Environment.GetEnvironmentVariable("OPENWEATHER_API_KEY")
                ?? Environment.GetEnvironmentVariable("WEATHERAPI_KEY")
                ?? "7fd005b949b6f2a40a2ee72012fd13f0";
        }

private static void AddOnce(List<string> list, string s)
{
    if (!list.Contains(s)) list.Add(s);
}

        /// <summary>
        /// 获取城市未来 N 天逐日天气预报。
        /// </summary>
        /// <param name="city">城市名称（中文或英文，例如：Shanghai/上海）</param>
        /// <param name="days">天数，1-14，默认 7 天</param>
        [HttpGet("forecast")]
        public async Task<ApiResult<List<DailyForecastDto>>> GetForecast([FromQuery] string city, [FromQuery] int days = 7)
        {
            var result = new ApiResult<List<DailyForecastDto>>();
            if (string.IsNullOrWhiteSpace(city))
            {
                result.msg = "城市名称不能为空";
                return result;
            }
            if (days < 1 || days > 14) days = 7;

            var cacheKey = $"weather:{city}:{days}";
            if (_cache.TryGetValue(cacheKey, out List<DailyForecastDto>? cached))
            {
                result.success(cached, "OK(缓存)");
                return result;
            }

            try
            {
                var http = _httpClientFactory.CreateClient();
                // 1) 城市转经纬度
                var geoUrl = $"https://geocoding-api.open-meteo.com/v1/search?name={Uri.EscapeDataString(city)}&count=1&language=zh&format=json";
                var geoResp = await http.GetAsync(geoUrl);
                geoResp.EnsureSuccessStatusCode();
                var geoJson = await geoResp.Content.ReadAsStringAsync();
                var geo = JsonSerializer.Deserialize<GeoResponse>(geoJson, JsonOptions);
                if (geo == null || geo.results == null || geo.results.Length == 0)
                {
                    result.msg = "未找到该城市的经纬度信息";
                    return result;
                }
                var (lat, lon) = (geo.results[0].latitude, geo.results[0].longitude);

                // 2) 逐日天气
                var forecastUrl = $"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&daily=temperature_2m_max,temperature_2m_min,weathercode,precipitation_sum,precipitation_probability_max,windspeed_10m_max&timezone=auto&forecast_days={days}";
                var fcResp = await http.GetAsync(forecastUrl);
                fcResp.EnsureSuccessStatusCode();
                var fcJson = await fcResp.Content.ReadAsStringAsync();
                var forecast = JsonSerializer.Deserialize<ForecastResponse>(fcJson, JsonOptions);
                if (forecast?.daily == null || forecast.daily.time == null)
                {
                    result.msg = "天气数据解析失败";
                    return result;
                }

                var count = forecast.daily.time.Length;
                var list = new List<DailyForecastDto>(count);
                for (int i = 0; i < count; i++)
                {
                    list.Add(new DailyForecastDto
                    {
                        Date = forecast.daily.time[i],
                        TempMaxC = SafeAt(forecast.daily.temperature_2m_max, i),
                        TempMinC = SafeAt(forecast.daily.temperature_2m_min, i),
                        WeatherCode = (int)SafeAt(forecast.daily.weathercode, i),
                        WeatherText = MapWeatherCode((int)SafeAt(forecast.daily.weathercode, i)),
                        PrecipMm = SafeAt(forecast.daily.precipitation_sum, i),
                        PrecipProb = SafeAt(forecast.daily.precipitation_probability_max, i),
                        WindMax = SafeAt(forecast.daily.windspeed_10m_max, i)
                    });
                }

                // 缓存 10 分钟
                _cache.Set(cacheKey, list, TimeSpan.FromMinutes(10));
                result.success(list, "OK");
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取天气失败 city={City} days={Days}", city, days);
                // 交给全局异常过滤器统一格式，但这里也给出友好信息
                result.msg = "获取天气失败，请稍后再试";
                return result;
            }
        }

        private static double SafeAt(double[]? arr, int i) => (arr != null && i < arr.Length) ? arr[i] : double.NaN;
        private static int SafeAt(int[]? arr, int i) => (arr != null && i < arr.Length) ? arr[i] : 0;

        // 统一评估每日预警等级与条目（Open‑Meteo 为主：使用温度、降水量/概率、风速以及 WMO 代码）
        private static (int level, List<string> items) EvaluateWarnings(DailyForecastDto d)
        {
            int level = 0;
            var items = new List<string>();
            if (d == null) return (0, new List<string> { "无数据" });

            // 高温（更严格分级）
            if (d.TempMaxC >= 37) { level = Math.Max(level, 3); AddOnce(items, "高温（≥37℃）"); }
            else if (d.TempMaxC >= 35) { level = Math.Max(level, 2); AddOnce(items, "高温（≥35℃）"); }

            // 低温
            if (d.TempMinC <= -10) { level = Math.Max(level, 3); AddOnce(items, "严寒（≤-10℃）"); }
            else if (d.TempMinC <= 0) { level = Math.Max(level, 2); AddOnce(items, "低温（≤0℃）"); }

            // 温差
            if (!double.IsNaN(d.TempMaxC) && !double.IsNaN(d.TempMinC) && (d.TempMaxC - d.TempMinC) >= 12)
            { level = Math.Max(level, 1); AddOnce(items, "温差较大（≥12℃）"); }

            // 降水（需要同时满足概率和量，减少误报）
            if (!double.IsNaN(d.PrecipProb) && !double.IsNaN(d.PrecipMm))
            {
                if (d.PrecipProb >= 80 && d.PrecipMm >= 10) { level = Math.Max(level, 3); AddOnce(items, $"强降雨（{d.PrecipMm:F1}mm，概率{d.PrecipProb}%）"); }
                else if (d.PrecipProb >= 60 && d.PrecipMm >= 5) { level = Math.Max(level, 2); AddOnce(items, $"降雨（{d.PrecipMm:F1}mm，概率{d.PrecipProb}%）"); }
                else if (d.PrecipProb >= 40 && d.PrecipMm >= 1) { level = Math.Max(level, 1); AddOnce(items, $"可能降雨（{d.PrecipMm:F1}mm，概率{d.PrecipProb}%）"); }
            }

            // 风（10m 最大风速，单位 m/s）
            if (!double.IsNaN(d.WindMax))
            {
                if (d.WindMax >= 20) { level = Math.Max(level, 3); AddOnce(items, $"大风（{d.WindMax:F1} m/s）"); }
                else if (d.WindMax >= 12) { level = Math.Max(level, 2); AddOnce(items, $"较大风（{d.WindMax:F1} m/s）"); }
            }

            // WMO 代码（强对流/雪）
            var code = d.WeatherCode;
            if (code == 96 || code == 99) { level = Math.Max(level, 3); AddOnce(items, "雷暴伴冰雹"); }
            else if (code == 95) { level = Math.Max(level, 2); AddOnce(items, "雷暴"); }
            else if (code == 71 || code == 73 || code == 75 || code == 85 || code == 86)
            {
                // 有雪类代码时，若降水概率>=40更可信
                if (double.IsNaN(d.PrecipProb) || d.PrecipProb >= 40) level = Math.Max(level, 2);
                AddOnce(items, "降雪");
            }
            else if (code == 61 || code == 63 || code == 65 || code == 80 || code == 81 || code == 82)
            {
                if (double.IsNaN(d.PrecipProb) || d.PrecipProb >= 40) level = Math.Max(level, 1);
                AddOnce(items, "降雨迹象");
            }

            // 兼容 OpenWeather 条件码范围（若有）
            if (code >= 200 && code <= 232) { level = Math.Max(level, 3); AddOnce(items, "雷暴"); }
            else if (code >= 600 && code <= 622) { level = Math.Max(level, 2); AddOnce(items, "降雪"); }
            else if (code >= 500 && code <= 531) { level = Math.Max(level, 1); AddOnce(items, "降雨迹象"); }

            if (items.Count == 0) items.Add("无明显风险");
            return (level, items);
        }

        private static string MapWeatherCode(int code) => code switch
        {
            0 => "晴朗",
            1 or 2 => "多云",
            3 => "阴",
            45 or 48 => "雾/冰雾",
            51 or 53 or 55 => "毛毛雨",
            56 or 57 => "冻毛毛雨",
            61 or 63 or 65 => "小/中/大雨",
            66 or 67 => "冻雨",
            71 or 73 or 75 => "小/中/大雪",
            77 => "雪粒",
            80 or 81 or 82 => "阵雨",
            85 or 86 => "阵雪",
            95 => "雷阵雨",
            96 or 99 => "雷暴伴有冰雹",
            _ => "未知"
        };

        // 将常见 HTTP 状态码映射为可读提示
        private static string MapHttpError(string stage, HttpStatusCode status)
        {
            int code = (int)status;
            if (code == 429) return $"{stage}失败：请求过于频繁(429)";
            return status switch
            {
                HttpStatusCode.Unauthorized => $"{stage}失败：API Key 无效或未提供(401)",
                HttpStatusCode.Forbidden => $"{stage}失败：权限不足/配额受限(403)",
                HttpStatusCode.NotFound => $"{stage}失败：资源不存在(404)",
                _ when code >= 500 && code < 600 => $"{stage}失败：服务端错误({code})",
                _ => $"{stage}失败：HTTP {code}"
            };
        }

        private static readonly JsonSerializerOptions JsonOptions = new()
        {
            PropertyNameCaseInsensitive = true
        };

        /// <summary>
        /// 未来一周（7天）天气预警（数据源：Open‑Meteo，免 Key）。
        /// 规则：
        /// - 高温预警：最高温 ≥ 35℃（红色：≥37℃）
        /// - 低温预警：最低温 ≤ 0℃
        /// - 温差预警：日温差 ≥ 12℃
        /// - 天气现象：基于 WMO weathercode（并兼容 OpenWeather 代码范围映射）
        /// </summary>
        /// <param name="city">城市名称</param>
        [HttpGet("weekly-warning")]
        public async Task<ApiResult<WeeklyWarningDto>> GetWeeklyWarning([FromQuery] string city)
        {
            var res = new ApiResult<WeeklyWarningDto>();
            if (string.IsNullOrWhiteSpace(city))
            {
                res.msg = "城市名称不能为空";
                return res;
            }

            try
            {
                var cacheKey = $"weekly-warning:om:{city}";
                if (_cache.TryGetValue(cacheKey, out WeeklyWarningDto? cached))
                {
                    res.success(cached!, "OK(缓存)");
                    return res;
                }

                // 直接使用 Open‑Meteo 的免费逐日预报
                var fcResult = await GetForecast(city, 7);
                var data = fcResult.data ?? new List<DailyForecastDto>();
                if (data.Count == 0)
                {
                    res.msg = string.IsNullOrEmpty(fcResult.msg) ? "未获取到天气数据" : fcResult.msg;
                    return res;
                }

                var details = new List<DayWarning>();
                int maxLevel = 0;
                foreach (var d in data)
                {
                    var (level, items) = EvaluateWarnings(d);
                    maxLevel = Math.Max(maxLevel, level);
                    details.Add(new DayWarning { Date = d.Date ?? string.Empty, Level = level, Items = items });
                }

                var summary = maxLevel switch
                {
                    0 => "本周整体风险较低。",
                    1 => "本周可能有轻微不利天气，请留意温差/降雨等。",
                    2 => "本周较大可能出现不利天气（如高温/降雨/降雪等），请关注防护。",
                    _ => "本周存在较高风险天气，请及时做好防护。"
                };

                var dto = new WeeklyWarningDto
                {
                    City = city,
                    Days = data.Count,
                    MaxLevel = maxLevel,
                    Summary = summary,
                    Details = details
                };

                _cache.Set(cacheKey, dto, TimeSpan.FromMinutes(10));
                res.success(dto, "OK(Free/Open‑Meteo)");
                return res;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "每周天气预警失败(Open‑Meteo) city={City}", city);
                res.msg = "天气预警服务暂不可用";
                return res;
            }
        }

        private async Task<ApiResult<WeeklyWarningDto>> BuildWeeklyByFallback(string city, string msg)
        {
            var res = new ApiResult<WeeklyWarningDto>();
            try
            {
                // 使用现有的 Open‑Meteo 逐日接口作为回退数据源
                var fcResult = await GetForecast(city, 7);
                var data = fcResult.data ?? new List<DailyForecastDto>();
                if (data.Count == 0)
                {
                    res.msg = msg;
                    return res;
                }

                int maxLevel = 0;
                var details = new List<DayWarning>();
                foreach (var d in data)
                {
                    var (level, items) = EvaluateWarnings(d);
                    maxLevel = Math.Max(maxLevel, level);
                    details.Add(new DayWarning { Date = d.Date ?? string.Empty, Level = level, Items = items });
                }

                var summary = maxLevel switch
                {
                    0 => "本周整体风险较低。(回退)",
                    1 => "本周可能有轻微不利天气，请留意温差/降雨等。(回退)",
                    2 => "本周较大可能出现不利天气（如高温/降雨/降雪等），请关注防护。(回退)",
                    _ => "本周存在较高风险天气，请及时做好防护。(回退)"
                };

                var dto = new WeeklyWarningDto
                {
                    City = city,
                    Days = data.Count,
                    MaxLevel = maxLevel,
                    Summary = summary,
                    Details = details
                };

                res.success(dto, msg);
                return res;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "回退(Open-Meteo)失败 city={City}", city);
                res.msg = msg;
                return res;
            }
        }

        /// <summary>
        /// 按经纬度查询未来 N 天（默认 3 天）天气，基于 Open‑Meteo（免 Key）。
        /// 返回格式化字符串：当前温度 + 未来N天最高/最低温。
        /// </summary>
        /// <param name="latitude">纬度</param>
        /// <param name="longitude">经度</param>
        /// <param name="days">天数(1-14)，默认 3</param>
        // 已移除对外端点：请使用 get-forecast-by-city。以下方法改为 private，且无路由特性。
        private async Task<ApiResult<string>> GetForecastByCoordinate([FromQuery] double latitude, [FromQuery] double longitude, [FromQuery] int days = 3)
        {
            var res = new ApiResult<string>();
            if (days < 1 || days > 14) days = 3;

            string cacheKey = $"openmeteo:{latitude:F4},{longitude:F4}:{days}";
            if (_cache.TryGetValue(cacheKey, out string? cachedText))
            {
                res.success(cachedText!, "OK(缓存)");
                return res;
            }

            try
            {
                var http = _httpClientFactory.CreateClient();
                var url = $"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current_weather=true&daily=temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days={days}";
                var response = await http.GetAsync(url);
                response.EnsureSuccessStatusCode();
                var json = await response.Content.ReadAsStringAsync();
                var data = JsonSerializer.Deserialize<OmForecastResponse>(json, JsonOptions);
                if (data?.daily?.time == null)
                {
                    res.msg = "无法解析天气数据";
                    return res;
                }

                var text = BuildForecastText(data, days);
                _cache.Set(cacheKey, text, TimeSpan.FromMinutes(10));
                res.success(text, "OK");
                return res;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Open-Meteo 获取失败 lat={Lat}, lon={Lon}", latitude, longitude);
                res.msg = "天气服务暂不可用";
                return res;
            }
        }

        /// <summary>
        /// 按“城市名”查询未来 N 天（默认 3 天）天气，自动地理编码后获取预报，返回同样的格式化字符串。
        /// </summary>
        /// <param name="city">城市名（支持中文/英文）</param>
        /// <param name="days">天数(1-14)，默认 3</param>
        [HttpGet("get-forecast-by-city")]
        public async Task<ApiResult<string>> GetForecastByCity([FromQuery] string city, [FromQuery] int days = 3)
        {
            var res = new ApiResult<string>();
            if (string.IsNullOrWhiteSpace(city))
            {
                res.msg = "城市名称不能为空";
                return res;
            }
            if (days < 1 || days > 14) days = 3;

            var cacheKey = $"openmeteo:city:{city}:{days}";
            if (_cache.TryGetValue(cacheKey, out string? cached))
            {
                res.success(cached!, "OK(缓存)");
                return res;
            }

            try
            {
                var http = _httpClientFactory.CreateClient();
                // 1) 城市 -> 经纬度（Open-Meteo Geocoding）
                var geoUrl = $"https://geocoding-api.open-meteo.com/v1/search?name={Uri.EscapeDataString(city)}&count=1&language=zh&format=json";
                var geoResp = await http.GetAsync(geoUrl);
                geoResp.EnsureSuccessStatusCode();
                var geoJson = await geoResp.Content.ReadAsStringAsync();
                var geo = JsonSerializer.Deserialize<GeoResponse>(geoJson, JsonOptions);
                if (geo?.results == null || geo.results.Length == 0)
                {
                    res.msg = "未找到该城市的经纬度信息";
                    return res;
                }
                var lat = geo.results[0].latitude;
                var lon = geo.results[0].longitude;

                // 2) 获取预报
                var fcUrl = $"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current_weather=true&daily=temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days={days}";
                var fcResp = await http.GetAsync(fcUrl);
                fcResp.EnsureSuccessStatusCode();
                var fcJson = await fcResp.Content.ReadAsStringAsync();
                var data = JsonSerializer.Deserialize<OmForecastResponse>(fcJson, JsonOptions);
                if (data?.daily?.time == null)
                {
                    res.msg = "无法解析天气数据";
                    return res;
                }

                var text = BuildForecastText(data, days);
                _cache.Set(cacheKey, text, TimeSpan.FromMinutes(10));
                res.success(text, "OK");
                return res;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Open-Meteo 城市查询失败 city={City}", city);
                res.msg = "天气服务暂不可用";
                return res;
            }
        }

        private static string BuildForecastText(OmForecastResponse data, int days)
        {
            var sb = new StringBuilder();
            var currentTemp = data.current_weather?.temperature;
            if (currentTemp.HasValue)
                sb.AppendLine($"当前温度: {currentTemp.Value}℃");
            else
                sb.AppendLine("当前温度: --℃");

            sb.AppendLine("未来天气预报:");
            var count = Math.Min(days, data.daily!.time!.Length);
            for (int i = 0; i < count; i++)
            {
                var date = data.daily!.time![i];
                var max = SafeAt(data.daily!.temperature_2m_max, i);
                var min = SafeAt(data.daily!.temperature_2m_min, i);
                sb.AppendLine($"{date} | 最高 {max}℃ 最低 {min}℃");
            }
            return sb.ToString();
        }

        /// <summary>
        /// 全国风向分布统计（免 Key）。
        /// - 默认统计一组常见城市（北上广深杭成渝等）。
        /// - 也可通过 cities 传入逗号分隔的城市名列表，例如："北京,上海,广州"。
        /// 返回各风向所占百分比（四舍五入到整数），适配环形图展示。
        /// </summary>
        /// <param name="cities">可选，逗号分隔的城市名</param>
        [HttpGet("wind-direction-stats")]
        public async Task<ApiResult<List<WindDirectionStat>>> GetWindDirectionStats([FromQuery] string? cities = null)
        {
            var res = new ApiResult<List<WindDirectionStat>>();
            var cityList = string.IsNullOrWhiteSpace(cities)
                ? DefaultCities
                : cities.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();

            // 规范化用于缓存的键：排序后拼接，提升命中率
            var normalized = cityList.Select(c => c.Trim()).OrderBy(c => c).ToList();
            var cacheKey = $"openmeteo:windstats:{string.Join('|', normalized)}";
            if (_cache.TryGetValue(cacheKey, out List<WindDirectionStat>? cached))
            {
                res.success(cached!, "OK(缓存)");
                return res;
            }

            try
            {
                var http = _httpClientFactory.CreateClient();
                var buckets = new Dictionary<string, int>(StringComparer.Ordinal)
                {
                    ["北风"] = 0,
                    ["东北风"] = 0,
                    ["东风"] = 0,
                    ["东南风"] = 0,
                    ["南风"] = 0,
                    ["西南风"] = 0,
                    ["西风"] = 0,
                    ["西北风"] = 0,
                    ["微风"] = 0
                };

                foreach (var city in cityList)
                {
                    // 1) 城市 -> 经纬度
                    var geoUrl = $"https://geocoding-api.open-meteo.com/v1/search?name={Uri.EscapeDataString(city)}&count=1&language=zh&format=json";
                    var geoResp = await http.GetAsync(geoUrl);
                    if (!geoResp.IsSuccessStatusCode) continue;
                    var geoJson = await geoResp.Content.ReadAsStringAsync();
                    var geo = JsonSerializer.Deserialize<GeoResponse>(geoJson, JsonOptions);
                    if (geo?.results == null || geo.results.Length == 0) continue;
                    var (lat, lon) = (geo.results[0].latitude, geo.results[0].longitude);

                    // 2) 当前天气（获取当前风速/风向）
                    var url = $"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current_weather=true&timezone=auto";
                    var resp = await http.GetAsync(url);
                    if (!resp.IsSuccessStatusCode) continue;
                    var json = await resp.Content.ReadAsStringAsync();
                    var data = JsonSerializer.Deserialize<OmForecastResponse>(json, JsonOptions);
                    var cw = data?.current_weather;
                    if (cw == null) continue;

                    var dirName = MapWindDirection(cw.windspeed, cw.winddirection);
                    if (!string.IsNullOrEmpty(dirName)) buckets[dirName]++;
                }

                var total = buckets.Values.Sum();
                if (total == 0) total = 1; // 防止除零

                var list = buckets.Select(kv => new WindDirectionStat
                {
                    Name = kv.Key,
                    Count = kv.Value,
                    Percent = Math.Round(kv.Value * 100.0 / total, 0)
                })
                // 环形图更常见顺序：按百分比降序
                .OrderByDescending(x => x.Percent)
                .ToList();

                _cache.Set(cacheKey, list, TimeSpan.FromMinutes(10));
                res.success(list, "OK");
                return res;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "全国风向统计失败 cities={Cities}", cities);
                res.msg = "风向统计服务暂不可用";
                return res;
            }
        }

        private static string MapWindDirection(double speed, double degree)
        {
            // 微风：风速 < 1.5 m/s（可调整）
            if (speed < 1.5) return "微风";
            // 分成 8 个象限（22.5°边界）
            double d = (degree % 360 + 360) % 360;
            if (d >= 337.5 || d < 22.5) return "北风";
            if (d < 67.5) return "东北风";
            if (d < 112.5) return "东风";
            if (d < 157.5) return "东南风";
            if (d < 202.5) return "南风";
            if (d < 247.5) return "西南风";
            if (d < 292.5) return "西风";
            return "西北风";
        }

        private static readonly List<string> DefaultCities = new()
        {
            // 覆盖全国各省级行政区的省会/直辖市/特区，保证默认为“全国样本”
            "北京","天津","上海","重庆",
            "石家庄","太原","呼和浩特","沈阳","长春","哈尔滨",
            "南京","杭州","合肥","福州","南昌","济南","郑州","武汉","长沙",
            "广州","南宁","海口",
            "成都","贵阳","昆明",
            "西安","兰州","西宁","拉萨","银川","乌鲁木齐",
            "台北","香港","澳门"
        };

        #region DTO & Response Models
        /// <summary>
        /// 一周天气预警（聚合）
        /// </summary>
        public class WeeklyWarningDto
        {
            public string City { get; set; } = string.Empty;
            public int Days { get; set; }
            /// <summary>本周最高预警等级（0-3）</summary>
            public int MaxLevel { get; set; }
            /// <summary>汇总描述</summary>
            public string Summary { get; set; } = string.Empty;
            /// <summary>逐日明细</summary>
            public List<DayWarning> Details { get; set; } = new();
        }

        /// <summary>
        /// 每日预警明细
        /// </summary>
        public class DayWarning
        {
            /// <summary>日期(yyyy-MM-dd)</summary>
            public string Date { get; set; } = string.Empty;
            /// <summary>预警等级（0-3）</summary>
            public int Level { get; set; }
            /// <summary>预警条目（人类可读）</summary>
            public List<string> Items { get; set; } = new();
        }
        public class DailyForecastDto
        {
            /// <summary>日期(yyyy-MM-dd)</summary>
            public string? Date { get; set; }
            /// <summary>最高温(℃)</summary>
            public double TempMaxC { get; set; }
            /// <summary>最低温(℃)</summary>
            public double TempMinC { get; set; }
            /// <summary>天气代码</summary>
            public int WeatherCode { get; set; }
            /// <summary>天气描述</summary>
            public string? WeatherText { get; set; }
            /// <summary>当日累计降水量(mm)</summary>
            public double PrecipMm { get; set; }
            /// <summary>当日最大降水概率(%)</summary>
            public double PrecipProb { get; set; }
            /// <summary>当日10米高度最大风速(m/s)</summary>
            public double WindMax { get; set; }
        }

        /// <summary>
        /// 风向统计结果
        /// </summary>
        public class WindDirectionStat
        {
            public string Name { get; set; } = string.Empty;
            public int Count { get; set; }
            public double Percent { get; set; }
        }

        private class GeoResponse
        {
            public GeoItem[]? results { get; set; }
        }
        private class GeoItem
        {
            public double latitude { get; set; }
            public double longitude { get; set; }
        }
        private class ForecastResponse
        {
            public ForecastDaily? daily { get; set; }
        }
        private class ForecastDaily
        {
            public string[]? time { get; set; }
            public double[]? temperature_2m_max { get; set; }
            public double[]? temperature_2m_min { get; set; }
            public int[]? weathercode { get; set; }
            public double[]? precipitation_sum { get; set; }
            public int[]? precipitation_probability_max { get; set; }
            public double[]? windspeed_10m_max { get; set; }
        }
        
        // Open‑Meteo 经纬度接口（含当前与逐日）
        private class OmForecastResponse
        {
            public OmCurrentWeather? current_weather { get; set; }
            public ForecastDaily? daily { get; set; }
        }
        private class OmCurrentWeather
        {
            public double temperature { get; set; }
            public double windspeed { get; set; }
            public double winddirection { get; set; }
        }

        // OpenWeather Geocoding & One Call DTOs
        private class OwDirectItem
        {
            public double lat { get; set; }
            public double lon { get; set; }
            public string? name { get; set; }
            public string? country { get; set; }
        }

        private class OwOneCall
        {
            public List<OwDaily>? daily { get; set; }
        }

        private class OwDaily
        {
            public long dt { get; set; }
            public OwTemp? temp { get; set; }
            public List<OwWeather>? weather { get; set; }
        }

        private class OwTemp
        {
            public double min { get; set; }
            public double max { get; set; }
        }

        private class OwWeather
        {
            public int id { get; set; }
            public string? main { get; set; }
            public string? description { get; set; }
        }
        #endregion
    }
}
